package com.gitee.apanlh.util.reflection;

import com.gitee.apanlh.util.base.Empty;
import com.gitee.apanlh.util.base.IteratorUtils;
import com.gitee.apanlh.util.base.StringUtils;
import com.gitee.apanlh.util.cache.local.Cache;
import com.gitee.apanlh.util.cache.local.CacheUtils;
import com.gitee.apanlh.util.valid.ValidParam;

import java.lang.reflect.Field;
import java.util.List;

/**	
 * 	反射字段缓存
 * 	
 * 	@author Pan
 */
public class FieldCache implements FieldProvider {
	
	/** 类+默认构造函数缓存 */
	static final Cache<Class<?>, Field[]> 		  FIELDS_BASE_CACHE = CacheUtils.cache(256);
	/** 类+构造函数缓存 */
	static final Cache<String, FieldCacheObject>  FIELDS_CACHE 	  	= CacheUtils.cache(512);
	
	/**
	 * 	获取字段缓存
	 * 	<br>根据类及字段获取字段配置
	 * 	<br>自定义是否忽略静态字段
	 * 	
	 * 	@author Pan
	 * 	@param 	clazz		类
	 * 	@return	FieldConfig
	 */
	public FieldCacheObject getCache(Class<?> clazz) {
		return getCache(clazz, true, null);
	}
	
	/**
	 * 	获取字段缓存
	 * 	<br>根据类及字段获取字段配置
	 * 	<br>自定义获取忽略静态字段
	 * 
	 * 	@author Pan
	 * 	@param 	clazz				类
	 * 	@param 	ignoreStaticField	true忽略静态字段
	 * 	@return	FieldConfig
	 */
	public FieldCacheObject getCache(Class<?> clazz, boolean ignoreStaticField) {
		return getCache(clazz, ignoreStaticField, null);
	}
	
	/**
	 * 	获取字段缓存
	 * 	<br>根据类及字段获取字段配置
	 * 	<br>自定义获取忽略静态字段
	 * 	<br>自定义对象
	 * 	
	 * 	@author Pan
	 * 	@param  <T>      数据类型
	 * 	@param 	clazz				类
	 * 	@param 	ignoreStaticField	true忽略静态字段
	 * 	@param 	obj					对象
	 * 	@return	FieldConfig
	 */
	public <T> FieldCacheObject getCache(Class<?> clazz, boolean ignoreStaticField, T obj) {
		return FIELDS_CACHE.get(createKey(clazz, ignoreStaticField, obj), () -> FieldCacheObject.create(clazz, ignoreStaticField));
	}
	
	@Override
	public Field getField(Class<?> clazz, String fieldName) {
		return getCache(clazz, false, null).getFieldMap().get(fieldName);
	}
	
	@Override
	public Field[] getDeclaredFields(Class<?> clazz) {
		return FIELDS_BASE_CACHE.get(clazz, clazz::getDeclaredFields);
	}
	
	@Override
	public <T> Field[] getFields(T t) {
		if (t == null) {
			return Empty.arrayObject(Field.class);
		}
		
		return getFields(t.getClass());
	}
	
	@Override
	public <T> Field[] getFields(T t, boolean ignoreStaticField) {
		if (t == null) {
			return Empty.arrayObject(Field.class);
		}
		
		return getFields(t.getClass(), ignoreStaticField);
	}
	
	@Override
	public <T> Field[] getFields(T t, FieldCacheObject fieldCacheObject) {
		if (t == null) {
			return Empty.arrayObject(Field.class);
		}
		
		return getFields(t.getClass(), fieldCacheObject);
	}
	
	@Override
	public <T> Field[] getFields(List<T> list) {
		if (list == null) {
			return Empty.arrayObject(Field.class);
		}
		
		return getFields(list, true);
	}
	
	@Override
	public <T> Field[] getFields(List<T> list, boolean ignoreStaticField) {
		T first = IteratorUtils.getFirst(list);
		if (first == null) {
			return Empty.arrayObject(Field.class);
		}
		
		return getFields(first, ignoreStaticField);
	}
	
	@Override
	public <T> Field[] getFields(List<T> list, FieldCacheObject fieldCacheObject) {
		T first = IteratorUtils.getFirst(list);
		if (first == null) {
			return Empty.arrayObject(Field.class);
		}
		
		return getFields(first.getClass(), fieldCacheObject);
	}
	
	@Override
	public Field[] getFields(Class<?> clazz) {
		return getFields(clazz, true);
	}
	
	@Override
	public Field[] getFields(Class<?> clazz, boolean ignoreStaticField) {
		return getCache(clazz, ignoreStaticField, null).getFields();
	}
	
	@Override
	public Field[] getFields(Class<?> clazz, FieldCacheObject fieldCacheObject) {	
		return FIELDS_CACHE.get(fieldCacheObject.getCacheKey(), () -> FIELDS_CACHE.put(fieldCacheObject.getCacheKey(), fieldCacheObject)).getFields();
	}
	
	/**	
	 * 	生成Key
	 * 	<br>支持动态类型(String/String[]/List)
	 * 	
	 * 	@author Pan
	 * 	@param 	clazz				类
	 * 	@param 	ignoreStaticField	true开启忽略静态字段
	 * 	@param 	obj					忽略字段
	 * 	@return	String
	 */
	@SuppressWarnings("unchecked")
	static <T> String createKey(Class<?> clazz, boolean ignoreStaticField, T obj) {
		StringBuilder builder = StringUtils.createBuilder();
		builder.append(ClassUtils.getName(clazz)).append("#").append(ignoreStaticField).append("#");
		
		if (obj == null && ignoreStaticField) {
			return builder.toString();
		}
		
		if (obj instanceof String) {
			String s = (String) obj;
			
			if (ValidParam.isEmpty(s)) {
				return builder.toString();
			}
			builder.append(s);
		}
		
		if (obj instanceof String[]) {
			String[] array = (String[]) obj;
			
			if (ValidParam.isEmpty(array)) {
				return builder.toString();
			}
			IteratorUtils.array(array, e -> builder.append(e).append("#"));
		}
		
		if (obj instanceof List) {
			List<String> list = (List<String>) obj;
			
			if (ValidParam.isEmpty(list)) {
				return builder.toString();
			}
			IteratorUtils.array(list, e -> builder.append(e).append("#"));
		}
		return builder.toString();
	}
}
